﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Controls.Maps;
using System.Device.Location;
using Microsoft.Phone.Tasks;
using System.Windows.Navigation;
using Microsoft.Phone.Controls.Maps.Platform;

namespace Nok_Air.BingMap
{
    public partial class AirPortBingMap : PhoneApplicationPage
    {
        private GeocodeService.GeocodeServiceClient geocodeService = null;
        private RouteService.RouteServiceClient routeService = null;
        private GeoCoordinateWatcher watcher = null;
        private GeoCoordinate _airprotGeo = null;
        private GeoCoordinate _userGeo = null;
        private LocationRect AirportLocation = null;
        private LocationRect UserLocation = null;
        private int index;
        private double lat;
        private double lon;
        public AirPortBingMap()
        {
            InitializeComponent();
           // geocodeResults = new GeocodeService.GeocodeResult[2];
            reverseGeocodeResults = new GeocodeService.GeocodeResult[2];
          
            myBingMap.Loaded += new RoutedEventHandler(myBingMap_Loaded);

            // Create the service variable and set the callback method using the GeocodeCompleted property.
            geocodeService = new GeocodeService.GeocodeServiceClient("BasicHttpBinding_IGeocodeService");
            // Create the service variable and set the callback method using the CalculateRouteCompleted property.
            routeService = new RouteService.RouteServiceClient("BasicHttpBinding_IRouteService");          
        }

        void t_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Microsoft.Phone.Tasks.PhoneCallTask call = new PhoneCallTask();
            call.PhoneNumber = App.ViewModel.AirportItems[index].AirPortPhone;
            call.DisplayName = App.ViewModel.AirportItems[index].AirPortPhoneName;
            call.Show();
        }

        private void myBingMap_Loaded(object sender, System.Windows.RoutedEventArgs e)
        {
            ObtainCurrentLocation();
            //remove event
            myBingMap.Loaded -= new RoutedEventHandler(myBingMap_Loaded);
            lat = double.Parse(App.ViewModel.AirportItems[index].AirPortLat);
            lon = double.Parse(App.ViewModel.AirportItems[index].AirPortLon);
            _airprotGeo = new GeoCoordinate(lat, lon);
            AirportLocation = new LocationRect(lat, lon, lat, lon);
            myBingMap.SetView(AirportLocation);
            //  myBingMap.ZoomLevel = 15;
            Pushpin pin = new Pushpin() { Location = _airprotGeo };
            TextBlock t = new TextBlock() { Height = 100, Width = 200, Text = App.ViewModel.AirportItems[index].AirPortName, Margin = new Thickness(5), TextWrapping = TextWrapping.Wrap, FontFamily = new FontFamily("PSD Display"), FontSize = 25 };
            t.MouseLeftButtonUp += new MouseButtonEventHandler(t_MouseLeftButtonUp);
            pin.Content = t;
            pin.Width = 250;
            pin.Height = 200;

            PushpinLayer.Children.Add(pin);
           // myBingMap.Children.Add(pin);
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);
            string selectedIndex = "";
            if (NavigationContext.QueryString.TryGetValue("selectedItem", out selectedIndex))
            {
                index = int.Parse(selectedIndex);
            }
        }        

        #region get user location
        private void ObtainCurrentLocation()
        {
            watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.Default);
            watcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(watcher_PositionChanged);
            watcher.Start();
        }

        private void watcher_PositionChanged(object sender,GeoPositionChangedEventArgs<GeoCoordinate> e)
        {
            Deployment.Current.Dispatcher.BeginInvoke(() => ChangePosition(e));
        }

        private void ChangePosition(GeoPositionChangedEventArgs<GeoCoordinate> e)
        {            
            //stop & clean up, we don't need it anymore
            watcher.Stop();
            watcher.Dispose();
            watcher = null;

            _userGeo = e.Position.Location;
            UserLocation = new LocationRect(_userGeo.Latitude, _userGeo.Longitude, _userGeo.Latitude, _userGeo.Longitude);

            //get geoCode result
            ReverseGeocode(_userGeo.Latitude, _userGeo.Longitude, 0);
            ReverseGeocode(_airprotGeo.Latitude, _airprotGeo.Longitude, 1);

            Pushpin pin = new Pushpin() { Location = new GeoCoordinate(_userGeo.Latitude, _userGeo.Longitude) };
            pin.Content = new Image() { Source = new System.Windows.Media.Imaging.BitmapImage(new Uri("/Images/BingMapIcons/ApplicationBar/Location.png", UriKind.Relative)), Width = 60, Height = 60 };

            myBingMap.Children.Add(pin);

            //create list for set view
            GeoCoordinate[] geoList = new GeoCoordinate[2];
            geoList[0] = _airprotGeo;
            geoList[1] = _userGeo;
            myBingMap.SetView(LocationRect.CreateLocationRect(geoList));
        }

        #endregion
        private void ButtonZoomIn_Click(object sender, RoutedEventArgs e)
        {
            myBingMap.ZoomLevel += 1;
        }

        private void ButtonZoomOut_Click(object sender, RoutedEventArgs e)
        {
            myBingMap.ZoomLevel -= 1;
        }

        private void ButtonMode_Click(object sender, EventArgs e)
        {
            if (myBingMap.Mode is AerialMode)
            {
                myBingMap.Mode = new RoadMode();
            }
            else
            {
                myBingMap.Mode = new AerialMode() { ShouldDisplayLabels = false };
            }
        }

        private void YourLocation_Click(object sender, EventArgs e)
        {
            if (UserLocation == null) return;
           // myBingMap.ZoomLevel = 1;
            myBingMap.SetView(UserLocation);
           // myBingMap.ZoomLevel = 15;
        }

        private void AirportLocation_Click(object sender, EventArgs e)
        {
            if (AirportLocation == null) return;
            //  myBingMap.ZoomLevel = 1;
            myBingMap.SetView(AirportLocation);
            //  myBingMap.ZoomLevel = 15;
        }

        private void ButtonDirections_Click(object sender, EventArgs e)
        {
            if (AirportLocation == null || UserLocation == null) return;
            //  myBingMap.ZoomLevel = 1;
            //create list for set view
            GeoCoordinate[] geoList = new GeoCoordinate[2];
            geoList[0] = _airprotGeo;
            geoList[1] = _userGeo;
            myBingMap.SetView(LocationRect.CreateLocationRect(geoList));
        }

        #region ReverseGeocode
        // This method accepts a reverseGeocode Location string as well as a ‘waypoint index’, which will be used to track each asynchronous reverseGeocode request.
        private void ReverseGeocode(double _lat, double _long, int waypointIndex)
        {           
            geocodeService.ReverseGeocodeCompleted += new EventHandler<GeocodeService.ReverseGeocodeCompletedEventArgs>(geocodeService_reverseGeocodeCompleted);

            // Set the credentials and the reverseGeocode query, which could be an latitude and longitude.
            GeocodeService.ReverseGeocodeRequest geocodeRequest = new GeocodeService.ReverseGeocodeRequest();
            geocodeRequest.Credentials = new Credentials();
            geocodeRequest.Credentials.ApplicationId = ((ApplicationIdCredentialsProvider)myBingMap.CredentialsProvider).ApplicationId;
            geocodeRequest.Location = new Location() { Latitude = _lat, Longitude = _long };

            // Make the asynchronous ReverseGeocode request, using the ‘waypoint index’ as 
            // the user state to track this request and allow it to be identified when the response is returned.
            geocodeService.ReverseGeocodeAsync(geocodeRequest, waypointIndex);
        }

        // This is the global internal variable where results are stored. These are accessed later to calculate the route.
        internal GeocodeService.GeocodeResult[] reverseGeocodeResults;

        // This is the Geocode request callback method.
        private void geocodeService_reverseGeocodeCompleted(object sender, GeocodeService.ReverseGeocodeCompletedEventArgs e)
        {
            //remove event
            geocodeService.ReverseGeocodeCompleted -= new EventHandler<GeocodeService.ReverseGeocodeCompletedEventArgs>(geocodeService_reverseGeocodeCompleted);            
          
            // Retrieve the user state of this response (the ‘waypoint index’) to identify which geocode request 
            //   it corresponds to.
            int waypointIndex = System.Convert.ToInt32(e.UserState);

            // Retrieve the GeocodeResult for this response and store it in the global variable geocodeResults, using
            //   the waypoint index to position it in the array.
            reverseGeocodeResults[waypointIndex] = e.Result.Results[0];

            // Look at each element in the global gecodeResults array to figure out if more geocode responses still 
            //   need to be returned.

            bool doneGeocoding = true;

            foreach (GeocodeService.GeocodeResult gr in reverseGeocodeResults)
            {
                if (gr == null)
                {
                    doneGeocoding = false;
                }
            }

            // If the geocodeResults array is totally filled, then calculate the route.
            if (doneGeocoding)
            {
                geocodeService.ReverseGeocodeCompleted -= new EventHandler<GeocodeService.ReverseGeocodeCompletedEventArgs>(geocodeService_reverseGeocodeCompleted);
                CalculateRoute(reverseGeocodeResults);
            }
            else
            {
                geocodeService.ReverseGeocodeCompleted += new EventHandler<GeocodeService.ReverseGeocodeCompletedEventArgs>(geocodeService_reverseGeocodeCompleted);
            }
        }
        #endregion

        #region Geocode
        // This method accepts a geocode query string as well as a ‘waypoint index’, which will be used to track each asynchronous geocode request.
        private void Geocode(string strAddress, int waypointIndex)
        {
            geocodeService.GeocodeCompleted += new EventHandler<GeocodeService.GeocodeCompletedEventArgs>(geocodeService_GeocodeCompleted);

            // Set the credentials and the geocode query, which could be an address or location.
            GeocodeService.GeocodeRequest geocodeRequest = new GeocodeService.GeocodeRequest();
            geocodeRequest.Credentials = new Credentials();
            geocodeRequest.Credentials.ApplicationId = ((ApplicationIdCredentialsProvider)myBingMap.CredentialsProvider).ApplicationId;
            geocodeRequest.Query = strAddress;

            // Make the asynchronous Geocode request, using the ‘waypoint index’ as 
            //   the user state to track this request and allow it to be identified when the response is returned.
            geocodeService.GeocodeAsync(geocodeRequest, waypointIndex);
        }

        // This is the global internal variable where results are stored. These are accessed later to calculate the route.
        internal GeocodeService.GeocodeResult[] geocodeResults;

        // This is the Geocode request callback method.
        private void geocodeService_GeocodeCompleted(object sender, GeocodeService.GeocodeCompletedEventArgs e)
        {
            geocodeService.GeocodeCompleted -= new EventHandler<GeocodeService.GeocodeCompletedEventArgs>(geocodeService_GeocodeCompleted);

            // Retrieve the user state of this response (the ‘waypoint index’) to identify which geocode request 
            //   it corresponds to.
            int waypointIndex = System.Convert.ToInt32(e.UserState);

            // Retrieve the GeocodeResult for this response and store it in the global variable geocodeResults, using
            //   the waypoint index to position it in the array.
            geocodeResults[waypointIndex] = e.Result.Results[0];

            // Look at each element in the global gecodeResults array to figure out if more geocode responses still 
            //   need to be returned.

            bool doneGeocoding = true;

            foreach (GeocodeService.GeocodeResult gr in geocodeResults)
            {
                if (gr == null)
                {
                    doneGeocoding = false;
                }
            }

            // If the geocodeResults array is totally filled, then calculate the route.
            if (doneGeocoding)
                CalculateRoute(geocodeResults);

        }
        /*
        private void btnCalculateRoute_Click(object sender, RoutedEventArgs e)
        {
            // Initialize the length of the results array. In this sample we have two waypoints.
            geocodeResults = new GeocodeService.GeocodeResult[2];

            // Make the two Geocode requests using the values of the text boxes. Also pass the waypoint indexes 
            //   of these two values within the route.
            Geocode("khon kaen", 0);
            Geocode("bangkok", 1);
        }
        */
#endregion

        #region CalculateRoute
        // This method makes the initial CalculateRoute asynchronous request using the results of the Geocode Service.
        private void CalculateRoute(GeocodeService.GeocodeResult[] results)
        {
            routeService.CalculateRouteCompleted += new EventHandler<RouteService.CalculateRouteCompletedEventArgs>(routeService_CalculateRouteCompleted);

            // Set the credentials.
            RouteService.RouteRequest routeRequest = new RouteService.RouteRequest();
            routeRequest.Credentials = new Credentials();
            routeRequest.Credentials.ApplicationId = ((ApplicationIdCredentialsProvider)myBingMap.CredentialsProvider).ApplicationId;

            // Return the route points so the route can be drawn.
            routeRequest.Options = new RouteService.RouteOptions();
            routeRequest.Options.RoutePathType = RouteService.RoutePathType.Points;

            // Set the waypoints of the route to be calculated using the Geocode Service results stored in the geocodeResults variable.
            routeRequest.Waypoints = new System.Collections.ObjectModel.ObservableCollection<RouteService.Waypoint>();
            foreach (GeocodeService.GeocodeResult result in results)
            {
                routeRequest.Waypoints.Add(GeocodeResultToWaypoint(result));
            }

            // Make the CalculateRoute asnychronous request.
            routeService.CalculateRouteAsync(routeRequest);
        }

        private RouteService.Waypoint GeocodeResultToWaypoint(GeocodeService.GeocodeResult result)
        {
            RouteService.Waypoint waypoint = new RouteService.Waypoint();
            waypoint.Description = result.DisplayName;
            waypoint.Location = new Location();
            waypoint.Location.Latitude = result.Locations[0].Latitude;
            waypoint.Location.Longitude = result.Locations[0].Longitude;
            return waypoint;
        }

        // This is the callback method for the CalculateRoute request.
        private void routeService_CalculateRouteCompleted(object sender, RouteService.CalculateRouteCompletedEventArgs e)
        {
            routeService.CalculateRouteCompleted -= new EventHandler<RouteService.CalculateRouteCompletedEventArgs>(routeService_CalculateRouteCompleted);

            // If the route calculate was a success and contains a route, then draw the route on the map.
            if ((e.Result.ResponseSummary.StatusCode == RouteService.ResponseStatusCode.Success) & (e.Result.Result.Legs.Count != 0))
            {             
                // Set properties of the route line you want to draw.
                Color routeColor = Colors.Blue;
                SolidColorBrush routeBrush = new SolidColorBrush(routeColor);
                MapPolyline routeLine = new MapPolyline();
                routeLine.Locations = new LocationCollection();
                routeLine.Stroke = routeBrush;
                routeLine.Opacity = 0.65;
                routeLine.StrokeThickness = 5.0;

                // Retrieve the route points that define the shape of the route.
                foreach (Location p in e.Result.Result.RoutePath.Points)
                {
                    routeLine.Locations.Add(new Location() { Latitude = p.Latitude, Longitude = p.Longitude });
                }

                // Add a map layer in which to draw the route.
                // MapLayer myRouteLayer = new MapLayer();
                // myBingMap.Children.Add(myRouteLayer);

                // Add the route line to the new layer.
                DirectionLayer.Children.Add(routeLine);

                // Figure the rectangle which encompasses the route. This is used later to set the map view.
                // LocationRect rect = new LocationRect(routeLine.Locations[0], routeLine.Locations[routeLine.Locations.Count - 1]);
                LocationRect rect = new LocationRect(routeLine.Locations[(routeLine.Locations.Count / 2) - 1], 5, 5);

                // For each geocode result (which are the waypoints of the route), draw a dot on the map.

                // foreach (GeocodeService.GeocodeResult gr in geocodeResults)
                foreach (GeocodeService.GeocodeResult gr in reverseGeocodeResults)
                {
                    Ellipse point = new Ellipse();
                    point.Width = 10;
                    point.Height = 10;
                    point.Fill = new SolidColorBrush(Colors.Red);
                    point.Opacity = 0.65;
                    Location location = new Location() { Latitude = gr.Locations[0].Latitude, Longitude = gr.Locations[0].Longitude };
                    //  MapLayer.SetPosition(point, location);
                    //  MapLayer.SetPositionOrigin(point, PositionOrigin.Center);

                    var geo = new GeoCoordinate(gr.Locations[0].Latitude, gr.Locations[0].Longitude);
                    // update pushpin location and show 
                    point.SetValue(MapLayer.PositionProperty, geo);

                    // Add the drawn point to the route layer.                    
                    //  myRouteLayer.Children.Add(point);
                    DirectionLayer.Children.Add(point);
                }
                // Set the map view using the rectangle which bounds the rendered route.
                myBingMap.SetView(rect);
                PBar.Visibility = Visibility.Collapsed;
            }
        }
        #endregion
    }
}